package defectsim;

import java.awt.Toolkit;
import java.io.IOException;

import repast.simphony.context.Context;
import repast.simphony.context.space.continuous.ContinuousSpaceFactoryFinder;
import repast.simphony.context.space.grid.GridFactoryFinder;
import repast.simphony.dataLoader.ContextBuilder;
import repast.simphony.engine.environment.RunEnvironment;
import repast.simphony.engine.schedule.ISchedule;
import repast.simphony.engine.schedule.ScheduleParameters;
import repast.simphony.parameter.Parameters;
import repast.simphony.random.RandomHelper;
import repast.simphony.space.grid.GridBuilderParameters;
import repast.simphony.valueLayer.GridValueLayer;
import defectsim.agents.DefectAgent;
import defectsim.agents.DefectCartesianAdder;
import defectsim.agents.DefectGridAdder;
import defectsim.agents.DislocationSource;
import defectsim.agents.PointDefectSource;


public class ContextCreator implements ContextBuilder<BaseAgent>{

	private SimulationEnvironment environment = SimulationEnvironment.getInstance();

	@SuppressWarnings("unchecked")
	public Context build(Context context) {

		initializeSimulatedEnvironment(context);

		setContextVariables(context);

		initializeSimulation(context);

		ISchedule schedule = RunEnvironment.getInstance().getCurrentSchedule();
		ScheduleParameters params = ScheduleParameters.createRepeating(1, 1, 0);
		schedule.schedule(params, this, "step");

		params = ScheduleParameters.createAtEnd(ScheduleParameters.LAST_PRIORITY);
		schedule.schedule(params, this, "endSimulation");

		return context;
	}

	@SuppressWarnings("unchecked")
	private void initializeSimulatedEnvironment(Context context) {
		environment.initialize();
		environment.setContext(context);
	}

	@SuppressWarnings("unchecked")
	private void setContextVariables(Context context) {
		Parameters p = RunEnvironment.getInstance().getParameters();
		int gridSize = (Integer)p.getValue("gridsize");
		double boxLength = (Double)p.getValue("spacelength");
		
		ContinuousSpaceFactoryFinder.createContinuousSpaceFactory(null)
		.createContinuousSpace(Globals.BOX_NAME, context, new DefectCartesianAdder<DefectAgent>(),
				new repast.simphony.space.continuous.WrapAroundBorders(), 
				boxLength, boxLength, 1);

		GridFactoryFinder.createGridFactory(null).createGrid(Globals.GRID_NAME, context,
				new GridBuilderParameters(new repast.simphony.space.grid.WrapAroundBorders(),
						new DefectGridAdder<DefectAgent>(), true, gridSize, gridSize));

		GridValueLayer stressValueLayer = new GridValueLayer(Globals.STRESS_VALUE_LAYER, true, 
				new repast.simphony.space.grid.WrapAroundBorders(), gridSize, gridSize);
		context.addValueLayer(stressValueLayer);
	}

	@SuppressWarnings("unchecked")
	private void initializeSimulation(Context context){
		Parameters p = RunEnvironment.getInstance().getParameters();
		
		// Ensure that the random seed parameter is the same in every run if random is not true
		boolean random = (Boolean)p.getValue("random");
		if (!random) {
			RandomHelper.setSeed(Globals.SEED_NUMBER);
		}
		
		// Simulation ends when cycle number reaches to total number of cycles
		int cyclicTickCount = (Integer)p.getValue("cyclictickcount");
		int nTotalCycle = (Integer)p.getValue("nototalcycle");
		RunEnvironment.getInstance().endAt(cyclicTickCount * nTotalCycle);
		
		// Add the dislocation source agent which generates and adds dislocations into the main context 
		// at the first simulation step. And then it generates dislocations in the following steps 
		// according to multiplication rules
		
		DislocationSource ds = new DislocationSource();
		
		// The following classes are used for testing purposes
//		DislocationPointSourceTester ds = new DislocationPointSourceTester();
//		DislocationSourceTester1 ds = new DislocationSourceTester1();
//		NoDislocationSourceTester ds = new NoDislocationSourceTester();
//		DislocationSourceTester4 ds = new DislocationSourceTester4();
		
		context.add(ds);
		// Add the point defect source agent which generates and adds point defects into the main context 
		// at the first simulation step. 
		PointDefectSource pds = new PointDefectSource();
//		PointDefectSourceTester pds = new PointDefectSourceTester();
		context.add(pds);
		// Add the resource context as a sub context which holds all forces
		DefectResourceContext drc = new DefectResourceContext(Globals.DEFECT_RESOURCE_CONTEXT_NAME);
		drc.Initialize();
		context.addSubContext(drc);
	}

	public void step(){
		environment.update();
	}

	public void endSimulation() throws IOException {
		environment.saveData();
		Toolkit.getDefaultToolkit().beep();
		Toolkit.getDefaultToolkit().beep();
		System.out.println("Simulation stopped!");
	}
}
